package de.dhbw.karlsruhe.picsim.gui;

import de.dhbw.karlsruhe.picsim.gui.editor.BreakPointEditor;
import de.dhbw.karlsruhe.picsim.gui.editor.DataMemoryEditor;
import de.dhbw.karlsruhe.picsim.gui.editor.HexEditor;
import de.dhbw.karlsruhe.picsim.gui.editor.StackEditor;
import de.dhbw.karlsruhe.picsim.gui.renderer.*;
import de.dhbw.karlsruhe.picsim.gui.util.DocumentAdapter;
import de.dhbw.karlsruhe.picsim.gui.util.GuiUtils;
import de.dhbw.karlsruhe.picsim.model.cpu.FileMutable;
import de.dhbw.karlsruhe.picsim.model.cpu.ReadOnlyFile;
import de.dhbw.karlsruhe.picsim.model.memory.DirectAddressAbleStack;
import de.dhbw.karlsruhe.picsim.model.memory.FileMemoryMutable;
import de.dhbw.karlsruhe.picsim.model.memory.RegisterValue;
import de.dhbw.karlsruhe.picsim.model.memory.impl.RegisterValueImpl;
import de.dhbw.karlsruhe.picsim.model.timer.Counter;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import javax.swing.event.DocumentListener;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import java.awt.*;

/**
 * Created by IntelliJ IDEA.
 * User: goesen
 * Date: Apr 15, 2010
 * Time: 5:03:32 PM
 */
@SuppressWarnings({"MagicNumber"})
public final class VisualisationFactoryImpl implements VisualisationFactory {
    private final ReadOnlyFile wRegister;

    private final JTextField wRegisterVisual;

    private final JTable programMemoryTable;
    private final Counter counter;
    private final JTable stackTable;
    private final JLabel time;
    private final JScrollPane dataMemoryVisual;
    private final DocumentListener listener;


    public VisualisationFactoryImpl(final CommandRegister guiData, final FileMemoryMutable fileMemory, final
    FileMutable wRegister, @NotNull final DirectAddressAbleStack stack, @NotNull final Counter counter
    ) {

        this.counter = counter;
        this.wRegister = wRegister;

        wRegisterVisual = new JTextField(1);
        wRegisterVisual.setText(String.valueOf(wRegister.getData().asInteger()));

        listener = new DocumentAdapter() {
            @Override
            protected void handleTextChange(@NonNls String newText) {
                final Integer integer = GuiUtils.userInputToInt(newText);
                if (integer != null) {
                    final RegisterValue value = RegisterValueImpl.createSafe(integer);
                    if (value != null) {
                        wRegister.setValue(value);
                    }
                }
            }
        };
        wRegisterVisual.getDocument().addDocumentListener(listener);


        programMemoryTable = new JTable(new ProgramMemoryTable(guiData));
        stackTable = new JTable(new SwingStack(stack));

        stackTable.getColumnModel().getColumn(0).setCellEditor(new StackEditor());


        time = new JLabel(String.valueOf(this.counter.getDoneCircles()));

        dataMemoryVisual = new JScrollPane();
        initializeDataMemory(fileMemory);
        dataMemoryVisual.setPreferredSize(new Dimension(220, Integer.MAX_VALUE));

    }

    private void initializeDataMemory(final FileMemoryMutable fileMemory) {
        final JScrollPane scrollPane = decorateDataTable(new JTable(new SwingFileMemory(fileMemory, 0)));
        final JTabbedPane jTabbedPane = new JTabbedPane();
        jTabbedPane.addTab("Bank 1", scrollPane);
        dataMemoryVisual.setViewportView(jTabbedPane);
        jTabbedPane.addTab("Bank2", decorateDataTable(new JTable(new SwingFileMemory(fileMemory, 128))));
    }

    @Override
    public JComponent getProgramMemoryVisualisation() {
        final TableColumnModel columnModel = programMemoryTable.getColumnModel();
        final TableColumn breakPoints = columnModel.getColumn(0);
        breakPoints.setPreferredWidth(40);

        breakPoints.setCellRenderer(new BreakPointRenderer());
        breakPoints.setCellEditor(new BreakPointEditor());
        final TableColumn numberColumn = columnModel.getColumn(1);
        numberColumn.setCellRenderer(new ProgramMemoryNumberRenderer());
        numberColumn.setCellEditor(new DefaultCellEditor(new JCheckBox()));
        final TableColumn commentColumn = columnModel.getColumn(3);
        commentColumn.setCellRenderer(new CommentRenderer());
        commentColumn.setPreferredWidth(600);
        final TableColumn opCodeColumn = columnModel.getColumn(2);
        opCodeColumn.setCellRenderer(new OpCodeRenderer());
        opCodeColumn.setMinWidth(40);
        opCodeColumn.setPreferredWidth(40);
        numberColumn.setPreferredWidth(40);
        numberColumn.setMinWidth(40);
        numberColumn.setPreferredWidth(40);
        numberColumn.setMaxWidth(75);

        final JScrollPane scrollPane = new JScrollPane(programMemoryTable);
        programMemoryTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        scrollPane.setPreferredSize(new Dimension(700, 800));
        programMemoryTable.setPreferredScrollableViewportSize(new Dimension(600, 200));


        return scrollPane;
    }

    @Override
    public JComponent getDataMemoryVisualisation() {


        return dataMemoryVisual;
    }

    @Override
    public JComponent getWRegisterVisualisation() {
        final JPanel panel = new JPanel(new GridBagLayout());

        final GridBagConstraints constraints = new GridBagConstraints();
        constraints.gridx = 1;
        constraints.ipadx = 30;
        panel.add(new JLabel("W-Register"), constraints);
        constraints.gridx++;

        panel.add(wRegisterVisual, constraints);
        return panel;
    }

    @Override
    public JComponent getStackVisualisation() {
        stackTable.setDefaultRenderer(Object.class, new StackRenderer());
        return new JScrollPane(stackTable);
    }

    @Override
    public JComponent getTimeVisualisation() {
        final JPanel panel = new JPanel();
        panel.add(new JLabel("Zeit"));
        panel.add(time);
        return panel;
    }

    @Override
    public void rebuild() {
        programMemoryTable.repaint();
        dataMemoryVisual.repaint();
        stackTable.repaint();
        /*
        bevor wir einen neuen Wert in das Wregister schreiben hängen wir den Listener ab,
          ansonsten denkt der, der User würde Zeug eintippen. Das macht zwar nichts, sieht aber dumm im Log aus
         */
        wRegisterVisual.getDocument().removeDocumentListener(listener);
        wRegisterVisual.setText(String.valueOf(wRegister.getData().asInteger()));
        wRegisterVisual.getDocument().addDocumentListener(listener);

        time.setText(String.valueOf(counter.getDoneCircles()));

    }

    private static JScrollPane decorateDataTable(final JTable table) {
        final TableColumnModel columnModel = table.getColumnModel();
        columnModel.getColumn(0).setCellRenderer(new FileAddressRenderer());
        columnModel.getColumn(1).setCellRenderer(new FileValueRenderer());
        columnModel.getColumn(1).setCellEditor(new DataMemoryEditor());
        columnModel.getColumn(0).setPreferredWidth(60);
        columnModel.getColumn(1).setPreferredWidth(40);
        final TableColumn hexColumn = columnModel.getColumn(2);
        hexColumn.setCellRenderer(new HexRenderer());
        hexColumn.setCellEditor(new HexEditor());
        table.setPreferredScrollableViewportSize(new Dimension(150, 200));
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        return new JScrollPane(table);
    }
}
